<!DOCTYPE html>
<html>
	<head>
		<!-- Import style -->
		<link rel="stylesheet" href="index.css" />
		<!-- Import Vue 3 -->
		<script src="vue.global.prod.js"></script>
		<!-- Import component library -->
		<script src="element-plus.js"></script>
		<link rel="stylesheet" href="./lib/codemirror.css">
		<script src=".lib/codemirror.js"></script>
		<script src=".lib/javascript/javascript.js"></script>
		<style scoped>
			.scrollbar-demo-item {
				display: flex;
				align-items: center;
				justify-content: space-between;
				height: 50px;
				margin: 10px;
				padding: 0 10px;
				border-radius: 4px;
				background: var(--el-color-primary-light-9);
				color: var(--el-color-primary);
			}

			.el-drawer__header {
				margin-bottom: 0;
			}

			.el-scrollbar {
				height: auto;
			}

			.CodeMirror {
				width: 100%;
				height: auto;
			}
		</style>
	</head>
	<body>
		<div id="app">
			<div class="common-layout" style="border: 1px solid var(--el-border-color)">
				<el-container>
					<el-aside width="300px" style="border-right: 1px solid var(--el-border-color)">
						<div style="display: flex;justify-content: space-between;align-items: center; padding: 10px;">
							<span style="flex: 1;">数据源</span>
							<el-button size="small" type="primary" @click="showView = 'data'">创建</el-button>
						</div>
						<div
							style="display: flex;align-items: center;padding: 10px;border-top: 1px solid var(--el-border-color);border-bottom: 1px solid var(--el-border-color);">
							<el-button size="small" type="success" @click="showView = 'item'">新建</el-button>
							<el-button size="small" text style="padding: 5px 0;">导入</el-button>
							<el-button size="small" text style="padding: 5px 0;">导出</el-button>
						</div>
						<el-scrollbar height="360px">
							<div v-for="item in tableData" :key="item" class="scrollbar-demo-item">
								<span>{{ item.name }}</span>
								<div style="display: flex;align-items: center;">
									<el-button size="small" text style="padding: 5px 0;">修改</el-button>
									<el-button size="small" text style="padding: 5px 0;">删除</el-button>
								</div>
							</div>
						</el-scrollbar>
					</el-aside>
					<el-main style="padding: 0;">
						<div
							style="display: flex;justify-content: space-between;align-items: center;border-bottom: 1px solid var(--el-border-color);padding: 10px;">
							<span style="flex: 1;">创建数据源</span>
							<el-button size="small" text @click="showView = ''">关闭</el-button>
						</div>
						<el-form ref="ruleFormRef" size="small" :model="showView == 'item'? FormData : schemadata"
							label-width="auto" style="max-width: 600px;padding: 10px;">
							<el-scrollbar height="400px">
								<template v-if="showView == 'data'">
									<el-form-item prop="collecton" label="表的名称"
										:rules="[{required: true, message: '请输入表名',trigger: 'blur'}]">
										<el-input type="text" v-model="schemadata.collecton"></el-input>
									</el-form-item>
									<el-form-item prop="description" label="表的描述">
										<el-input v-model="schemadata.description"></el-input>
									</el-form-item>
									<el-form-item prop="permission.read" label="读取权限">
										<el-input v-model="schemadata.permission.read"></el-input>
									</el-form-item>
									<el-form-item prop="permission.create" label="新增权限">
										<el-input v-model="schemadata.permission.create"></el-input>
									</el-form-item>
									<el-form-item prop="permission.update" label="更新权限">
										<el-input v-model="schemadata.permission.update"></el-input>
									</el-form-item>
									<el-form-item prop="permission.delete" label="删除权限">
										<el-input v-model="schemadata.permission.delete"></el-input>
									</el-form-item>
									<el-form-item prop="fieldRules" label="字段间校验">
										<el-radio-group v-model="enumType" @change="enumBtn">
											<el-radio-button value="fieldone" label="不选择"></el-radio-button>
											<el-radio-button value="arrayobject" label="选择校验"></el-radio-button>
										</el-radio-group>
										<div style="background-color: var(--el-color-info-light-9); padding: 8px;"
											v-if="enumType == 'arrayobject'">
											<el-row align="middle" :gutter="5">
												<template v-for="(item, index) in schemadata.fieldRules" :key="index">
													<el-col :span="10">
														<el-form-item label="校验规则"
															:prop="'fieldRules.' + index + '.rule'">
															<el-input v-model="item.fieldRules"
																placeholder="rule表达式，是一组js，返回值必须为true或false"></el-input>
														</el-form-item>
													</el-col>
													<el-col :span="10">
														<el-form-item label="错误信息"
															:prop="'fieldRules.' + index + '.errorMessage'">
															<el-input v-model=" item.errorMessage"
																placeholder="错误提示信息（仅在新增时生效，更新数据时不会提示此信息）"></el-input>
														</el-form-item>
													</el-col>
													<el-col :span="4">
														<el-button type="danger" @click="removeItem(index)"
															circle>—</el-button>
													</el-col>
												</template>
												<el-col>
													<el-button type="success" @click="addItem">添加</el-button>
												</el-col>
											</el-row>
										</div>
									</el-form-item>
									<el-button type="primary" @click="validateBtn('ext')">编写Schema扩展js</el-button>
								</template>
								<template v-if="showView == 'item'">
									<el-form-item prop="name" label="字段名称"
										:rules="[{required: true, message: '请输入字段名称',trigger: 'blur'}]">
										<el-input v-model="FormData.name"></el-input>
									</el-form-item>
									<el-form-item prop="bsonType" label="字段类型"
										:rules="[{required: true, message: '请选择字段类型'}]">
										<el-select v-model="FormData.bsonType" placeholder="请选择字段类型">
											<el-option v-for="item in getType" :label="item.label"
												:value="item.value"></el-option>
										</el-select>
									</el-form-item>
									<el-form-item prop="title" label="字段标题"
										:rules="[{required: true, message: '请输入字段名称',trigger: 'blur'}]">
										<el-input v-model="FormData.title" placeholder="开发者维护时自用"></el-input>
									</el-form-item>
									<el-form-item prop="required" label="必填提示">
										<el-input type="text" v-model="FormData.required"
											placeholder="为空不限制,约定是否必填"></el-input>
									</el-form-item>
									<el-form-item prop="permission" label="数据库权限">
										<el-input type="text" v-model="FormData.permission"
											placeholder="控制什么角色可以对什么数据进行读/写，可控制表和字段，可设置where条件">
											<template #append>
												<el-button>{/}</el-button>
											</template>
										</el-input>
									</el-form-item>
									<el-form-item prop="foreignKey" label="关联字段"
										:rules="[{required: enumType == 'data', message: '请输入字段名称',trigger: 'blur'}]">
										<el-input type="text" v-model="FormData.foreignKey"
											placeholder="值的格式为:表名.字段名"></el-input>
									</el-form-item>
									<el-form-item prop="componentForEdit" label="组件类型">
										<el-input type="text" v-model="FormData.componentForEdit"
											placeholder="使用什么组件渲染这个表单项"></el-input>
									</el-form-item>
									<el-form-item prop="search" label="搜索字段">
										<el-switch v-model="FormData.search"></el-switch>
									</el-form-item>
									<el-form-item prop="defaultValue" label="默认值">
										<el-input type="text" v-model="FormData.defaultValue" placeholder=""></el-input>
									</el-form-item>
									<el-form-item prop="forceDefaultValue.$env" label="强制默认值">
										<el-radio-group v-model="FormData.forceDefaultValue.$env">
											<el-radio-button value="one" label="不选择"></el-radio-button>
											<el-radio-button value="now" label="当前时间戳"></el-radio-button>
											<el-radio-button value="clientIP" label="当前客户端IP"></el-radio-button>
											<el-radio-button value="uid" label="当前用户Id"></el-radio-button>
										</el-radio-group>
									</el-form-item>
									<el-form-item prop="enum" label="枚举范围">
										<el-radio-group v-model="enumType" @change="enumBtn">
											<el-radio-button value="one" label="不选择"></el-radio-button>
											<el-radio-button value="array" label="简单数组"></el-radio-button>
											<el-radio-button value="object" label="对象数组"></el-radio-button>
											<el-radio-button value="data" label="数据表查询"></el-radio-button>
										</el-radio-group>
										<div style="background-color: var(--el-color-info-light-9); padding: 8px;"
											v-if="enumType != 'one'&& enumType != ''">
											<template v-if="enumType == 'data'">
												<el-form-item label-width="40px" label="表名" prop="enum.collection">
													<el-input v-model="FormData.enum.collection"
														placeholder="表的名称"></el-input>
												</el-form-item>
												<el-form-item style="margin: 5px 0;" label-width="60px" label="过滤别名"
													prop="name as value, name as text">
													<el-input v-model="FormData.enum.field"
														placeholder="格式first_letter asc|dasc"></el-input>
												</el-form-item>
												<el-form-item label-width="40px" label="排序" prop="enum.orderby">
													<el-input v-model="FormData.enum.orderby"
														placeholder="格式first_letter asc|dasc"></el-input>
												</el-form-item>
											</template>
											<el-row v-else align="middle" :gutter="5">
												<template v-for="(item, index) in FormData.enum" :key="index">
													<template v-if="enumType == 'array'">
														<el-col :span="20">
															<el-form-item label-width="0" :prop="'enum.' + index">
																<el-input v-model="item"
																	placeholder="数组中至少要有一个元素，且数组内的每一个元素都是唯一的。"></el-input>
															</el-form-item>
														</el-col>
													</template>

													<template v-else>
														<el-col :span="10">
															<el-form-item label-width="50px" :label="'text' + index"
																:prop="'enum.' + index + '.text'">
																<el-input v-model="item.text"
																	placeholder="text"></el-input>
															</el-form-item>
														</el-col>
														<el-col :span="10">
															<el-form-item label-width="50px" :label="'value' + index"
																:prop="'enum.' + index + '.value'">
																<el-input v-model=" item.value"
																	placeholder="value"></el-input>
															</el-form-item>
														</el-col>
													</template>
													<el-col :span="4">
														<el-button type="danger" @click="removeItem(index)"
															circle>—</el-button>
													</el-col>
												</template>
												<el-col>
													<el-button type="success" @click="addItem">添加</el-button>
												</el-col>
											</el-row>
										</div>
									</el-form-item>
									<el-form-item prop="enumType" label="树形结构">
										<el-select v-model="FormData.enumType"
											placeholder="字段值枚举类型，可选值tree。设为tree时，代表enum里的数据为树形结构">
											<el-option label="tree" value="tree"></el-option>
										</el-select>
									</el-form-item>
									<el-form-item v-if="FormData.enumType" prop="parentKey" label="父级的字段">
										<el-input type="text" v-model="FormData.parentKey"
											placeholder="树状数据查询的上级字段"></el-input>
									</el-form-item>
									<template v-if="FormData.bsonType==='int' || FormData.bsonType==='double'">
										<el-form-item prop="maximum" label="最大值">
											<el-input-number v-model="FormData.maximum"></el-input-number>
										</el-form-item>
										<el-form-item prop="exclusiveMaximum" label="排除maximum">
											<el-switch v-model="FormData.exclusiveMaximum"></el-switch>
										</el-form-item>
										<el-form-item prop="minimum" label="最小值">
											<el-input-number v-model="FormData.minimum"></el-input-number>
										</el-form-item>
										<el-form-item prop="exclusiveMinimum" label="排除minimum">
											<el-switch v-model="FormData.exclusiveMinimum"></el-switch>
										</el-form-item>
									</template>
									<el-form-item prop="minLength" label="最小长度">
										<el-input-number v-model="FormData.minLength"></el-input-number>
									</el-form-item>
									<el-form-item prop="maxLength" label="最大长度">
										<el-input-number v-model="FormData.maxLength"></el-input-number>
									</el-form-item>
									<el-form-item prop="pattern" label="正则表达式">
										<el-input type="text" v-model="FormData.pattern"
											placeholder="如设置为手机号的正则表达式后，不符合该正则表达式则校验失败，无法入库"></el-input>
									</el-form-item>
									<el-form-item prop="validateFunction" label="校验函数名">
										<el-input type="text" v-model="FormData.validateFunction" placeholder="扩展校验函数名">
											<template #append>
												<el-button @click="validateBtn">编辑代码</el-button>
											</template>
										</el-input>
									</el-form-item>
									<el-form-item prop="errorMessage" label="错误提示">
										<el-input type="text" v-model="FormData.errorMessage"
											placeholder="校验数据合法性失败后，返回的错误提示"></el-input>
									</el-form-item>

									<template v-if="FormData.bsonType==='array'">
										<el-form-item prop="arrayType" label="数组项类型">
											<el-select v-model="FormData.arrayType"
												placeholder="数组项类型，bsonType=array时有效">
												<el-option v-for="item in getType" :label="item.label"
													:value="item.value"></el-option>
											</el-select>
										</el-form-item>
									</template>
									<template v-if="FormData.bsonType==='file'">
										<el-form-item v-if="FormData.bsonType==='file'" prop="errorMessage"
											label="文件类型">
											<el-radio-group v-model="FormData.fileMediaType">
												<el-radio-button value="all" label="全部"></el-radio-button>
												<el-radio-button value="image" label="图片类型"></el-radio-button>
												<el-radio-button value="video" label="视频类型"></el-radio-button>
											</el-radio-group>
										</el-form-item>
										<el-form-item prop="fileExtName" label="扩展名过滤">
											<el-input type="text" v-model="FormData.fileExtName"
												placeholder="bsonType=file 时有效，多个文件扩展名用 ',' 分割，例如: jpg,png"></el-input>
										</el-form-item>
									</template>
									<template v-if="FormData.bsonType==='string'">
										<el-form-item prop="trim" label="去除空白字符">
											<el-radio-group v-model="FormData.trim"
												placeholder="去除空白字符，默认不去除，仅bsonType=string时有效">
												<el-radio-button label="不去除" value="none"></el-radio-button>
												<el-radio-button label="两端" value="both"></el-radio-button>
												<el-radio-button label="首端" value="start"></el-radio-button>
												<el-radio-button label="末尾" value="end"></el-radio-button>
											</el-radio-group>
										</el-form-item>
									</template>
									<el-form-item prop="description" label="字段描述">
										<el-input type="text" v-model="FormData.description"
											placeholder="开发者维护时自用。在生成前端表单代码时，如果字段未设置componentForEdit，且字段被渲染为input，那么input的placehold将默认为本描述"></el-input>
									</el-form-item>
								</template>
							</el-scrollbar>
							<el-form-item v-if="showView">
								<div style="justify-content: flex-end;display: flex;flex: 1;">
									<el-button @click="resetForm(ruleFormRef)">重置</el-button>
									<el-button type="primary" @click="submitForm(ruleFormRef)">添加</el-button>
								</div>
							</el-form-item>
						</el-form>
					</el-main>
				</el-container>
			</div>
			<el-drawer v-model="drawer" title="扩展校验函数" direction="rtl" size="80%">
				<el-input id="demotext" v-model="textarea" :autosize="{ minRows: 10, maxRows: 106 }"
					type="textarea"></el-input>
				<template #footer>
					<div style="flex: auto">
						<el-button @click="drawer = false">取消</el-button>
						<el-button type="primary" @click="confirmClick">保存</el-button>
					</div>
				</template>
			</el-drawer>
		</div>
		<script>
			const { createApp, ref, reactive, nextTick } = Vue
			const app = createApp({
				setup() {
					const ruleFormRef = ref()
					const showView = ref('')
					const tableData = ref([])
					const enumType = ref('')
					const drawer = ref(false)
					const editor = ref()
					const textarea = ref('')
					const schemadata = reactive({
						"collecton": '',
						"bsonType": "object",
						"required": [],
						"permission": {
							"read": '',
							"create": '',
							"update": '',
							"delete": ''
						},
						"properties": {
							"_id": { "description": "ID，系统自动生成" }
						}
					});
					const getType = ref([{
							label: '布尔值',
							value: 'bool'
						},
						{
							label: '字符串',
							value: 'string'
						},
						{
							label: '密码',
							value: 'password'
						},
						{
							label: '整数',
							value: 'int'
						},
						{
							label: '精度数',
							value: 'double'
						},
						{
							label: 'json对象',
							value: 'object'
						},
						{
							label: '文件',
							value: 'file'
						},
						{
							label: '数组',
							value: 'array'
						},
						{
							label: '时间戳',
							value: 'timestamp'
						},
						{
							label: '日期',
							value: 'date'
						}
					]);
					const FormData = reactive({
						name: '',
						bsonType: '',
						arrayType: '',
						title: '',
						description: '',
						defaultValue: '',
						forceDefaultValue: '',
						required: '',
						enum: '',
						enumType: '',
						fileMediaType: '',
						fileExtName: '',
						maximum: '',
						exclusiveMaximum: '',
						minimum: '',
						exclusiveMinimum: '',
						minLength: '',
						maxLength: '',
						trim: '',
						format: '',
						pattern: '',
						validateFunction: '',
						permission: '',
						errorMessage: '',
						foreignKey: '',
						parentKey: '',
						componentForEdit: '',
						search: false
					});

					const submitForm = async (formEl) => {
						if (!formEl) return
						await formEl.validate((valid, fields) => {
							if (valid) {
								if (showView.value === 'data') showView.value = ''
								tableData.value.push({ name: FormData.name })
								console.log(tableData.value)
								removeEmptyProperties(FormData)
								resetForm(formEl)
							} else {
								console.log('error submit!', valid, fields)
							}
						})
					}

					const resetForm = (formEl) => {
						if (!formEl) return
						formEl.resetFields()
					}
					const removeEmptyProperties = (objdata) => {
						const obj = { ...objdata };
						for (const key in obj) {
							if (obj[key] === null || obj[key] === undefined || obj[key] === '' || obj[key] === 0 ||
								obj[key] === false || obj[key] === {}) {
								delete obj[key];
							} else if (key === 'required') {
								schemadata.required.push(obj.name)
								obj.errorMessage = { required: "{title}" + obj[key] }
								delete obj[key];
							}
						}
						const name = obj.name
						delete obj.name;
						schemadata.properties[name] = obj
					}
					const enumBtn = (e) => {
						if (e === 'arrayobject') {
							schemadata.fieldRules = [{ rule: '', errorMessage: '' }];
							return
						}
						if (e === 'fieldone') {
							schemadata.fieldRules = ''
							return
						}
						FormData.enum = ''
						if (e == 'object' || e == 'array') {
							if (!Array.isArray(FormData.enum)) FormData.enum = []
							if (e == 'array') FormData.enum.push('')
							if (e == 'object') addItem()
						}
						if (e == 'data') FormData.enum = { "collection": "", "orderby": "", "field": "" }
					}

					function addItem() {
						if (enumType.value === 'arrayobject') {
							schemadata.fieldRules.push({ rule: '', errorMessage: '' });
						} else {
							FormData.enum.push({ text: '', value: '' });
						}
					}

					function removeItem(index) {
						if (enumType.value === 'arrayobject') {
							schemadata.fieldRules.splice(index, 1);
						} else {
							FormData.enum.splice(index, 1);
						}
					}

					function validateBtn(text) {
						drawer.value = true
						textarea.value = ''
						if (text == 'ext') {
							textarea.value = schemaext
						} else {
							textarea.value = validateText
						}
						nextTick(() => {
							if(editor.value)editor.value.toTextArea()
							editor.value = CodeMirror.fromTextArea(document.getElementById("demotext"), {
								lineWrapping: true,
								lineNumbers: true,
								mode: "javascript",
								matchBrackets: true
							});
						})

					}

					function confirmClick() {
						drawer.value = false
						textarea.value = ''
						const doc = editor.value.getValue()
						editor.value.toTextArea()
						console.log(doc)
					}
					return {
						drawer,
						getType,
						FormData,
						tableData,
						enumType,
						ruleFormRef,
						schemadata,
						resetForm,
						submitForm,
						showView,
						enumBtn,
						addItem,
						removeItem,
						confirmClick,
						validateBtn,
						textarea
					}
				}
			})
			app.use(ElementPlus);
			app.mount('#app')
const schemaext = `
module.exports = {
  trigger: {
	// 注册数据表的read前事件
	beforeRead: async function (
	// 确定要监听的什么样的JQL指令
	{
	  collection,
	  operation,
	  where,
	  field
	} = {}) {
		// 当上述jql指令被触发时，将执行这里的代码。这里就是普通的uniCloud代码，可以调用uniCloud的各种api。
		console.log("这个触发器被触发了")
	},
	afterRead: async function ({
	  collection,
	  operation,
	  where,
	  field
	} = {}) {

	}
  }
}
`
const validateText = `
// 扩展校验函数示例
module.exports = function (rule, value, data, callback) {
	// rule  当前规则
	// value 当前规则校验数据
	// data  全部校验数据
	// callback 可选，一般用于自定义 errorMessage，如果执行了callback return 值无效，callback 传入的 message 将替换 errorMessage
	// callback('message') 传入错误消息时校验不通过
	// callback() 无参时通过
	// 注意 callback 不支持异步调用，异步请使用 Promise/await/async
	return value.length < 10
}

// 异步校验 Promise
module.exports = function (rule, value, data) {
	return new Promise((resolve, reject) => {
	  setTimeout(() => {
		if (value > 10) {
		  // 校验通过
		  resolve()
		} else {
		  // 校验失败
		  resolve('error') // 等于 reject(new Error('error'))
		  // reject(new Error('error'))
		}
	  }, 3000);
	})
}

// 异步校验 await/async
module.exports = async function (rule, value, data) {
	let result = await uni.request({...})
	if (result > 10) {
	  // 校验通过
	  return true
	} else {
	  // 校验失败
	  return 'error message'
	}
}`
</script>
</body>
</html>